#ifdef WIN32

#include "plugin.h"

extern PAF_PE_FunctionTable video_get_ctrl_table(UINT8 stream_type);

#define AF_PE_PRINTF DBG_Printf
#define MEMCPY memcpy
#else
#include <types.h>
#include <sys_config.h>
#include <retcode.h>
#include <api/libc/string.h>
#include <api/libc/printf.h>
#include <osal/osal.h>
#include <hld/hld_dev.h>
#include <hld/dis/vpo.h>
#include <hld/decv/vdec_driver.h>
#include <hld/decv/decv.h>
#include <hld/deca/deca_dev.h>
#include <hld/deca/deca.h>
#include <hld/snd/snd_dev.h>
#include <hld/snd/snd.h>
#include <api/libtsi/db_3l.h>

#include <api/libmp/pe.h>
#include "video_engine.h"
#include "plugin.h"

#define AF_PE_PRINTF(...) //	libc_printf

#endif
#define PLAY_SPEED_MAX 6

static t2DecoderControl m_DecoderCtrl;
static AF_PE_PLAY_STATE m_CurPlaystate, m_NxtPlaystate;
static int m_CurPlaySpeed;
static DWORD m_SearchTime;
PAF_PE_FunctionTable pDecFuncTable; //decoder control services functions provided by decoder control library

#ifdef WIN32
static dec_cmd_sema = 0;
#define ENTER_DEC_CMD() {while(dec_cmd_sema){DecoderSleep(5);}; dec_cmd_sema = 1;}
#define LEAVE_DEC_CMD() {dec_cmd_sema = 0;}

#else
ID dec_cmd_sema_id = OSAL_INVALID_ID;

#define ENTER_DEC_CMD()	osal_semaphore_capture(dec_cmd_sema_id,TMO_FEVR)

#define LEAVE_DEC_CMD()	osal_semaphore_release(dec_cmd_sema_id)
#endif

#define MAX_PROG_TIME_NUM 64     //Fix bug:mp ts change prog subtitle can not show.

DWORD prog_time[MAX_PROG_TIME_NUM];
DWORD PEStreamStart(UINT8 video_stream_type)
{
#ifndef WIN32
    AF_PE_PRINTF("%s\n", __FUNCTION__);
#endif
    int i;
    for(i=0; i<20; i++)
        prog_time[i] = INVALID_TIME;
    m_CurPlaystate = PEPlay;
    m_NxtPlaystate = PEPlay;
    m_CurPlaySpeed = 1;
    m_SearchTime = INVALID_TIME;
    m_DecoderCtrl.PlayDir = FORWARD_PLAY;
    m_DecoderCtrl.ScrRatio = 0;
    m_DecoderCtrl.renew = FALSE;
    m_DecoderCtrl.subt_onoff = TRUE;
    m_DecoderCtrl.subp_stream_id = 0 ;
    m_DecoderCtrl.audio_stream_id = -1; // container should auto-select a correct audio track
    m_DecoderCtrl.VideoPlaymode =  VIDEO_PLAY_MODE_STOP;

    m_DecoderCtrl.subt_lang_id = -1;
    m_DecoderCtrl.prog_id = -1;
    pDecFuncTable = video_get_ctrl_table(video_stream_type);

    return 0;
}

DWORD PENormalPly()
{
#ifndef WIN32
    AF_PE_PRINTF("%s\n", __FUNCTION__);
#endif

    m_CurPlaySpeed = 1;
    m_SearchTime = INVALID_TIME;
    m_NxtPlaystate = PEPlay;
    
    return AF_ERR_PE_OK;
}

static DWORD PEFastForwardPly()
{
    DWORD ret;
    
#ifndef WIN32
    AF_PE_PRINTF("%s\n", __FUNCTION__);
#endif
    m_SearchTime = INVALID_TIME;
    
    if(m_CurPlaystate == PEPlay ||
       m_CurPlaystate == PEFFx2 || 
       m_CurPlaystate == PEFFx4 ||
       m_CurPlaystate == PEFFx8x16x32)
    {
       
        m_CurPlaySpeed++;
        if(m_CurPlaySpeed > PLAY_SPEED_MAX)
            m_CurPlaySpeed = 2;
    }
    else
        m_CurPlaySpeed = 2;
        
    switch(m_CurPlaySpeed)
    {
        case 1:
            return PENormalPly();
        case 2: 
            m_NxtPlaystate = PEFFx2;
            break;
        case 3:
            m_NxtPlaystate = PEFFx4;
            break;
        case 4:
        case 5:
        case 6:
        default:
            m_NxtPlaystate = PEFFx8x16x32;
            break;
    }
    return AF_ERR_PE_OK;
}

static DWORD PEFastReversePly()
{
    DWORD ret;

#ifndef WIN32
    AF_PE_PRINTF("%s\n", __FUNCTION__);
#endif

    m_SearchTime = INVALID_TIME;
    m_DecoderCtrl.PlayDir = BACKWARD_PLAY;

    if(m_CurPlaystate == PERev)
    {
        m_CurPlaySpeed++;
        if(m_CurPlaySpeed > PLAY_SPEED_MAX)
            m_CurPlaySpeed = 1;
    }
    else
        m_CurPlaySpeed = 1;
    
    /*
	In 2 speed FR play mode, FW should set video play mode to 
	VIDEO_PLAY_MODE_SKIP_B_P and ScrRatio to 2, because 
	we don't support 2 speed smooth FR playback. 
	*/
    if(m_CurPlaySpeed == 1)
        m_CurPlaySpeed = 2;

    m_NxtPlaystate = PERev;

    return AF_ERR_PE_OK;
}

static DWORD PESlowPly()
{
    DWORD ret;
    
#ifndef WIN32
    AF_PE_PRINTF("%s\n", __FUNCTION__);
#endif

    m_SearchTime = INVALID_TIME;

    if(m_CurPlaystate == PESlow)
    {
        m_CurPlaySpeed--;
        if(m_CurPlaySpeed < -3)
            m_CurPlaySpeed = -1;
    }
    else
        m_CurPlaySpeed = -1;
    

    m_NxtPlaystate = PESlow;
    
    return AF_ERR_PE_OK;
}

static DWORD PEPausePly()
{
    DWORD ret;
 
#ifndef WIN32	
    AF_PE_PRINTF("%s\n", __FUNCTION__);
#endif

    m_SearchTime = INVALID_TIME;

    m_CurPlaySpeed = 0xff;
    

    if(m_CurPlaystate != PEPauseStep)
    {
        m_NxtPlaystate = PEPauseStep;
    }
    return AF_ERR_PE_OK;
}

static DWORD PEStepPly()
{
    DWORD ret;
    
#ifndef WIN32
    AF_PE_PRINTF("%s\n", __FUNCTION__);
#endif

    m_SearchTime = INVALID_TIME;
    m_CurPlaySpeed = 0xfe;
    
    m_NxtPlaystate = PEPauseStep;
    return AF_ERR_PE_OK;
}

static DWORD PEStopPly()
{
    DWORD ret;
    
#ifndef WIN32
    AF_PE_PRINTF("%s\n", __FUNCTION__);
#endif

    m_SearchTime = INVALID_TIME;
    m_CurPlaySpeed = 1;

    m_NxtPlaystate = PEStop;
    return AF_ERR_PE_OK;
}

static DWORD PEResumeStopPly()
{
    DWORD ret;

#ifndef WIN32
    AF_PE_PRINTF("%s\n", __FUNCTION__);
#endif

    m_SearchTime = INVALID_TIME;
    m_CurPlaySpeed = 1;

    m_NxtPlaystate = PEResumeStop;
    return AF_ERR_PE_OK;
}

static DWORD PESeekPly(DWORD search_time)
{
    DWORD ret;
    
#ifndef WIN32
    AF_PE_PRINTF("%s: %d\n", __FUNCTION__, search_time);
#endif

    m_CurPlaySpeed = 1;
    m_SearchTime = search_time;
    m_NxtPlaystate = PEPlay;
    m_CurPlaystate = PEPlay;
    
    return AF_ERR_PE_OK;

}

static DWORD PEChangeProID(int prog_id, DWORD search_time)
{
    DWORD ret;
    
#ifndef WIN32    
    AF_PE_PRINTF("%s\n", __FUNCTION__);
#endif
    DEC_STREAM_INFO stream_info;
    MpgFileGetStreamInfo(&stream_info);

    if(stream_info.cur_prog_id != prog_id)
    {
        m_DecoderCtrl.prog_id = prog_id;
        m_DecoderCtrl.renew = TRUE;
        m_DecoderCtrl.SearchTime = INVALID_TIME;//search_time;
        m_DecoderCtrl.audio_stream_id = 0;
        m_DecoderCtrl.VideoPlaymode = VIDEO_PLAY_MODE_NORMAL;
        m_DecoderCtrl.AudioPlaymode = AUDIO_PLAY_MODE_ON;
        m_DecoderCtrl.PlayDir = FORWARD_PLAY;
        m_DecoderCtrl.ScrRatio = 1;
        m_CurPlaySpeed = 1;
        m_SearchTime = INVALID_TIME;
        m_NxtPlaystate = PEPlay;
        return AF_ERR_PE_OK;
    }
    else
        return AF_ERR_PE_FAIL;
}

static DWORD PEChangeSubtID(int subt_lang_id)
{
    DWORD ret;
    
#ifndef WIN32    
    AF_PE_PRINTF("%s\n", __FUNCTION__);
#endif

    if(m_DecoderCtrl.subt_lang_id != subt_lang_id)
    {
        m_DecoderCtrl.subt_lang_id = subt_lang_id;
        m_DecoderCtrl.renew = TRUE;
        //m_DecoderCtrl.AudioPlaymode = AUDIO_PLAY_MODE_NONE;
        m_DecoderCtrl.SearchTime = INVALID_TIME;
        return AF_ERR_PE_OK;
    }
    else
        return AF_ERR_PE_FAIL;
}

static DWORD PEChangeAID(int audio_stream_id)
{
    DWORD ret;
    
#ifndef WIN32    
    AF_PE_PRINTF("%s\n", __FUNCTION__);
#endif

    if(m_DecoderCtrl.audio_stream_id != audio_stream_id)
    {
        m_DecoderCtrl.audio_stream_id = audio_stream_id;
        m_DecoderCtrl.renew = TRUE;
        //m_DecoderCtrl.AudioPlaymode = AUDIO_PLAY_MODE_NONE;
        m_DecoderCtrl.SearchTime = INVALID_TIME;
        return AF_ERR_PE_OK;
    }
    else
        return AF_ERR_PE_FAIL;
}

static DWORD PEChangeSID(int sub_stream_id)
{
    DWORD ret;
    
#ifndef WIN32    
    AF_PE_PRINTF("%s\n", __FUNCTION__);
#endif

    if(m_DecoderCtrl.subp_stream_id != sub_stream_id)
    {
        m_DecoderCtrl.subp_stream_id = sub_stream_id;
        m_DecoderCtrl.renew = TRUE;
        m_DecoderCtrl.SearchTime = INVALID_TIME;
        return AF_ERR_PE_OK;
    }
    else
        return AF_ERR_PE_FAIL;
}

#if 0
static DWORD PEChangeVID(AF_PE_MESSAGE *pMessage)
{
    DWORD ret;
    
    AF_PE_PRINTF("%s\n", __FUNCTION__);

    if(m_DecoderCtrl.video_stream_id != pMessage->video_stream_id)
    {
        m_DecoderCtrl.video_stream_id = pMessage->video_stream_id;
        ret = UpdateStreamID();
    }
    else
        ret = AF_ERR_PE_OK;

    return ret;
}

static DWORD PEChangeSID(AF_PE_MESSAGE *pMessage)
{
    DWORD ret;
    
    AF_PE_PRINTF("%s: old id = 0x%x, new id = 0x%x\n", \
        __FUNCTION__, m_DecoderCtrl.subp_stream_id, pMessage->subp_stream_id);

    if(m_DecoderCtrl.subp_stream_id != pMessage->subp_stream_id)
    {
        m_DecoderCtrl.subp_stream_id = pMessage->subp_stream_id;
        ret = UpdateStreamID();
    }
    else
        ret = AF_ERR_PE_OK;

    return ret;
}

static DWORD PERstPlayPoint(AF_PE_MESSAGE *pMessage)
{
    DWORD ret = AF_ERR_PE_OK;

    if(pe_Dec_SetPlayPoint0(&pMessage->StartPoint, &pMessage->EndPoint, TRUE) == FALSE)
    {
        AF_PE_FWASSERT(0);
        ret = AF_ERR_NOT_AVAILABLE;
    }

    return ret;
}
#endif

AF_PE_PLAY_STATE get_cur_play_state()
{
    return m_CurPlaystate;
}


int get_cur_play_speed()
{
    return m_CurPlaySpeed;
}

AF_PE_PLAY_STATE get_next_play_state()
{
    return m_NxtPlaystate;
}

#ifndef WIN32
DWORD mpg_cmd_play_proc()
{
	DWORD ret;

    
    if(m_DecoderCtrl.renew == TRUE)
    {
        return AF_ERR_PE_FAIL;
    }
    ENTER_DEC_CMD();
    ret = PENormalPly();
    if(pDecFuncTable)
    {
        (*pDecFuncTable)[m_CurPlaystate][m_NxtPlaystate](&m_DecoderCtrl, m_CurPlaySpeed, m_SearchTime);
    }
    LEAVE_DEC_CMD();
    return ret;
}

DWORD mpg_cmd_pause_proc()
{
	DWORD ret;
    ENTER_DEC_CMD();
    ret = PEPausePly();
    if(pDecFuncTable)
    {
        (*pDecFuncTable)[m_CurPlaystate][m_NxtPlaystate](&m_DecoderCtrl, m_CurPlaySpeed, m_SearchTime);
    }
    LEAVE_DEC_CMD();
    return ret;
}

DWORD mpg_cmd_resume_proc()
{
	DWORD ret;
    ENTER_DEC_CMD();
    ret = PEStepPly();
    if(pDecFuncTable)
    {
        (*pDecFuncTable)[m_CurPlaystate][m_NxtPlaystate](&m_DecoderCtrl, m_CurPlaySpeed, m_SearchTime);
    }
    LEAVE_DEC_CMD();
    return ret;
}

DWORD mpg_cmd_ff_proc()
{
	DWORD ret;
    ENTER_DEC_CMD();
    ret = PEFastForwardPly();
    if(pDecFuncTable)
    {
        (*pDecFuncTable)[m_CurPlaystate][m_NxtPlaystate](&m_DecoderCtrl, m_CurPlaySpeed, m_SearchTime);
    }
    LEAVE_DEC_CMD();
    return ret;

}

DWORD mpg_cmd_fb_proc()
{
	DWORD ret;
    ENTER_DEC_CMD();
    ret = PEFastReversePly();
    if(pDecFuncTable)
    {
        (*pDecFuncTable)[m_CurPlaystate][m_NxtPlaystate](&m_DecoderCtrl, m_CurPlaySpeed, m_SearchTime);
    }
    LEAVE_DEC_CMD();
    return ret;

}

DWORD mpg_cmd_slow_proc()
{
	DWORD ret;
    ENTER_DEC_CMD();
    ret = PESlowPly();
    if(pDecFuncTable)
    {
        (*pDecFuncTable)[m_CurPlaystate][m_NxtPlaystate](&m_DecoderCtrl, m_CurPlaySpeed, m_SearchTime);
    }
    LEAVE_DEC_CMD();
    return ret;

}
extern BOOL g_VideoDecRunning;

DWORD mpg_cmd_stop_proc()
{
	DWORD ret;
    video_engine_tell_pecache_closing();
LBL_STOP:
    ENTER_DEC_CMD();
    ret = PEStopPly();
    if(pDecFuncTable)
    {
        (*pDecFuncTable)[m_CurPlaystate][m_NxtPlaystate](&m_DecoderCtrl, m_CurPlaySpeed, m_SearchTime);
    }
    else
    {
        if (g_VideoDecRunning)
        {
            struct vpo_device *vpo_dev;
            vpo_dev = (struct vpo_device *)dev_get_by_id(HLD_DEV_TYPE_DIS, 0);
            if (vpo_dev)
        	    vpo_win_onoff(vpo_dev, FALSE);
#ifdef DUAL_VIDEO_OUTPUT
            vpo_dev = (struct vpo_device *)dev_get_by_id(HLD_DEV_TYPE_DIS, 1);
            if (vpo_dev)
                vpo_win_onoff(vpo_dev, FALSE);	// avoid green screen
#endif
        }
    }
    LEAVE_DEC_CMD();
    
    while(g_VideoDecRunning)
    {
        osal_task_sleep(1);
        if (m_NxtPlaystate != PEStop)
            goto LBL_STOP;
    }

    return ret;

}

DWORD mpg_cmd_resume_stop_proc()
{
	DWORD ret, tCurPlaystate, tNxtPlaystate, tCurPlaySpeed, tSearchTime;
    ENTER_DEC_CMD();
    tCurPlaystate = m_CurPlaystate;
    tSearchTime = INVALID_TIME;
    tCurPlaySpeed = 1;
    tNxtPlaystate = PEResumeStop;
    if(pDecFuncTable)
    {
        if((*pDecFuncTable)[tCurPlaystate][tNxtPlaystate](&m_DecoderCtrl, tCurPlaySpeed, tSearchTime))
        {
            ret = PEResumeStopPly();
        }
        else
        {
            m_DecoderCtrl.renew = FALSE;
        }
    }
    
    LEAVE_DEC_CMD();
}

DWORD mpg_cmd_search_proc(DWORD search_time)
{
	DWORD ret;
    ENTER_DEC_CMD();
    ret = PESeekPly(search_time);
    if(pDecFuncTable)
    {
        (*pDecFuncTable)[m_CurPlaystate][m_NxtPlaystate](&m_DecoderCtrl, m_CurPlaySpeed, m_SearchTime);
    }
    LEAVE_DEC_CMD();
    return ret;
}


DWORD mpg_cmd_search_ms_proc(DWORD search_ms_time)
{
	DWORD ret;
    ENTER_DEC_CMD();
    ret = PESeekPly(search_ms_time | 0x80000000);
    if(pDecFuncTable)
    {
        (*pDecFuncTable)[m_CurPlaystate][m_NxtPlaystate](&m_DecoderCtrl, m_CurPlaySpeed, m_SearchTime);
    }
    LEAVE_DEC_CMD();
    return ret;
}

//DWORD mpg_cmd_change_audio_track(INT32 *aud_pid);
DWORD mpg_cmd_change_prog (int prog_id)
{
	DWORD ret = AF_ERR_PE_FAIL;
    DEC_STREAM_INFO stream_info;
    INT32   ch0 = 0;
    ENTER_DEC_CMD();
    MpgFileGetStreamInfo(&stream_info);

    if(stream_info.ProgNum > 1)
    {
        if( stream_info.cur_prog_id > (MAX_PROG_TIME_NUM - 1) )     //Fix bug:mp ts change prog subtitle can not show.
            ASSERT(0);
        prog_time[stream_info.cur_prog_id] = MPGFileDecoderGetPlayTime();
        if(prog_id < stream_info.ProgNum)      
             ret = PEChangeProID(prog_id, prog_time[prog_id]);
    }
    LEAVE_DEC_CMD();

//    mpg_cmd_change_audio_track(&ch0);
    return ret;
}

DWORD mpg_cmd_change_subt_lang (int subt_lang_id)
{
	DWORD ret = AF_ERR_PE_FAIL;
    DEC_STREAM_INFO stream_info;

    ENTER_DEC_CMD();
    MpgFileGetStreamInfo(&stream_info);

    if(stream_info.SubStreamNum > 1)
    {
        if(subt_lang_id < stream_info.SubStreamNum)      
             ret = PEChangeSubtID(subt_lang_id);   
    }
    LEAVE_DEC_CMD();
    
    return ret;
}

DWORD mpg_cmd_change_audio_track(INT32 *aud_pid)
{
    DWORD ret = AF_ERR_PE_FAIL;
    DEC_STREAM_INFO stream_info;

    ENTER_DEC_CMD();
    stream_info.AudioStreamNum = 0;
    MpgFileGetStreamInfo(&stream_info);

    if(stream_info.AudioStreamNum > 1)
    {
        if(*aud_pid > stream_info.AudioStreamNum)
        {            
             *aud_pid = 1;
        }
        ret = PEChangeAID(*aud_pid);
    }

    LEAVE_DEC_CMD();
    
    return ret;
}

DWORD mpg_cmd_change_subtitle(INT32 sub_pid)
{
    DWORD ret = AF_ERR_PE_FAIL;
    DEC_STREAM_INFO stream_info;

    ENTER_DEC_CMD();
    stream_info.SubStreamNum = 0;
    MpgFileGetStreamInfo(&stream_info);
    if(sub_pid >= 0 && sub_pid <= stream_info.SubStreamNum)
    {
        ret = PEChangeSID(sub_pid);
    }
    LEAVE_DEC_CMD();
    
    return ret;
}

#endif

/**********************TEST code for controlling mpeg/AVI trick mode***************/
#ifdef WIN32
DWORD s_time = 5;
BOOL SetCtrlCmd(DWORD msgCode, DWORD time)
{
    BOOL ret = TRUE;
    DEC_STREAM_INFO stream_info;
    ENTER_DEC_CMD();

    if(1)
    {
        switch(msgCode)
        {
            case V_KEY_PLAY:
                PENormalPly();
                break;
            case V_KEY_PAUSE:
                if(m_CurPlaystate == PEPauseStep)
                {
                    PEStepPly();
                }
                else
                {
                    PEPausePly();
                    //Delay for a while to show "Pause" OSD on PC version
                    DecoderSleep(100);
                }
                break;
            case V_KEY_FF: //FF
                PEFastForwardPly();
                break;
            case V_KEY_FB: //FB
                PEFastReversePly();
                break;
            case V_KEY_SLOW: //Slow
                PESlowPly();
                break;                
            case V_KEY_STOP:
                PEStopPly();
                break;
            case V_KEY_SEEK:
                PESeekPly(time);
                break;
            case V_KEY_CHGAUD:
                MpgFileGetStreamInfo(&stream_info);
                if(stream_info.AudioStreamNum)
                {
                    if(stream_info.cur_audio_stream_id < stream_info.AudioStreamNum)
                        ret = PEChangeAID(stream_info.cur_audio_stream_id+1);
                    else
                        ret = PEChangeAID(1);
                }
                if(ret == AF_ERR_PE_OK)
                    PESeekPly(MPGFileDecoderGetPlayTime());
                break;
        }
    }

    if(pDecFuncTable)
    {
        (*pDecFuncTable)[m_CurPlaystate][m_NxtPlaystate](&m_DecoderCtrl, m_CurPlaySpeed, m_SearchTime);
        ret = TRUE;
    }
    else
        ret = FALSE;
    
    LEAVE_DEC_CMD();
    return ret;
        
}
#endif
/**********************end of TEST code for controlling mpeg trick mode********/

/*----------------------------------------------------
Name: 
	GetCtrlCmd
Description: 
	Get control command sent by Firmware, this function
	is called by decoder and decoder should call this function
	2 to 3 times at least in order to resond command in time.
Parameters: 
    IN:
	    
    OUT:
	    *DecoderCtrl: decoder control structure pointer
Return: 
	TRUE: there is new command coming, otherwise, FALSE	
------------------------------------------------------*/
BOOL GetCtrlCmd(t2DecoderControl *DecoderCtrl)
{
    BOOL ret;
    ENTER_DEC_CMD();
	if (m_DecoderCtrl.renew)
	{
        MEMCPY(DecoderCtrl,  &m_DecoderCtrl, sizeof(t2DecoderControl));
		AF_PE_PRINTF("video_playmode = %d, audio_playmode = %d, scr_ratio = %d, searchtime = 0x%x\n", \
		m_DecoderCtrl.VideoPlaymode, m_DecoderCtrl.AudioPlaymode,	m_DecoderCtrl.ScrRatio, m_DecoderCtrl.SearchTime);
        m_CurPlaystate = m_NxtPlaystate;
        m_DecoderCtrl.renew = FALSE;
        m_SearchTime = INVALID_TIME;
		ret = TRUE;
	}
    else
        ret = FALSE;
    LEAVE_DEC_CMD();
    return ret;
}

